using SharpDX;
using System.Collections.Generic;

namespace Engine.FileFormats3D
{
    /// <summary>
    /// This is the middle Line, which is used to create roads
    /// </summary>
    public class MiddleLine
    {
        #region Public Methods

        public static float[] CalculateDistanceBetweenSegments(Vector3[] middle, out float lengthOfStreet)
        {
            lengthOfStreet = 0;
            float[] middleLineDistances = new float[middle.Length];
            middleLineDistances[0] = GeoMath.DistanceBtw2Points(middle[0], middle[1]);
            lengthOfStreet += middleLineDistances[0];
            for (int i2 = 1; i2 < middle.Length - 1; i2++)
            {
                middleLineDistances[i2] = GeoMath.DistanceBtw2Points(middle[i2], middle[i2 + 1]);
                lengthOfStreet += middleLineDistances[i2];
            }

            return middleLineDistances;
        }

        public static Vector3[] CreateNewMiddleLine(Vector3[] middle, decimal angleStart, decimal angleEnd, float offsetStart, float offsetEnd,
             float inclinationStart, float inclinationMiddleDegree, float inclinationEnd)
        {
            // Create the inclinationvectors for complete middleline.
            List<float> inclinationsDegree = new List<float>();
            float inclinationMiddle = inclinationMiddleDegree;
            float inclinationPointOne = inclinationStart;
            float inclinationPointTwo = inclinationEnd;
            List<float> distancesOnMiddleLine = new List<float>();

            distancesOnMiddleLine.Add(0);

            for (int i = 0; i < middle.Length - 1; i++)
            {
                distancesOnMiddleLine.Add(distancesOnMiddleLine[i] + GeoMath.DistanceBtw2Points(middle[i], middle[i + 1]));
            }

            inclinationsDegree.Add(inclinationPointOne);
            for (int i = 1; i < middle.Length - 1; i++)
            {
                float lengthOfStreet = distancesOnMiddleLine[distancesOnMiddleLine.Count - 1];
                float percentofStreet = distancesOnMiddleLine[i];// / lengthOfStreet;// / lengthOfStreet;
                float inclination;

                if (percentofStreet / lengthOfStreet < 0.50f)
                    inclination = inclinationPointOne + ((inclinationMiddle - inclinationPointOne) * (percentofStreet * 2.0f / lengthOfStreet));
                else
                    inclination = inclinationMiddle - ((inclinationPointTwo - inclinationMiddle) * (1.0f - (percentofStreet * 2.0f / lengthOfStreet)));

                inclinationsDegree.Add(inclination);
            }
            inclinationsDegree.Add(inclinationPointTwo);

            float[] offset = MiddleLine.Interpolate(middle, offsetStart, offsetEnd);
            Vector3[] newMiddleLine = new Vector3[middle.Length];

            // New position of the middleline + the transformation with road inclination.
            Vector3 vector = GeoMath.MakePointFromAngel(180 - angleStart, offset[0], inclinationsDegree[0]);

            newMiddleLine[0].X = middle[0].X - vector.X;
            newMiddleLine[0].Y = middle[0].Y - vector.Y;
            newMiddleLine[0].Z = middle[0].Z - vector.Z;

            for (int i = 1; i < newMiddleLine.Length - 1; i++)
            {
                decimal angle = (decimal)GeoMath.GetXYAngel(middle[i - 1], middle[i + 1]);
                vector = GeoMath.MakePointFromAngel(180 - angle, offset[i], inclinationsDegree[i]);

                newMiddleLine[i].X = middle[i].X + vector.X;
                newMiddleLine[i].Y = middle[i].Y - vector.Y;
                newMiddleLine[i].Z = middle[i].Z + vector.Z;
            }
            vector = GeoMath.MakePointFromAngel(180 - angleEnd, offset[offset.Length - 1], inclinationsDegree[inclinationsDegree.Count - 1]);

            newMiddleLine[middle.Length - 1].X = middle[middle.Length - 1].X + vector.X;
            newMiddleLine[middle.Length - 1].Y = middle[middle.Length - 1].Y - vector.Y;
            newMiddleLine[middle.Length - 1].Z = middle[middle.Length - 1].Z + vector.Z;

            return newMiddleLine;
        }

        public static Vector3[] CreateNewMiddleLineForForest(Vector3[] middle, float segmentLength)
        {
            for (int i = 0; i < middle.Length; i++)
            {
                middle[i].Y -= 1.2f;
            }

            List<Vector3> newMiddleLine = new List<Vector3>();
            bool isChanged = false;
            newMiddleLine.Add(middle[0]);

            for (int i = 1; i < middle.Length - 1; i++)
            {
                decimal angle = (decimal)GeoMath.GetXYAngel(newMiddleLine[newMiddleLine.Count - 1], middle[i]);

                if (GeoMath.DistanceBtw2Points(newMiddleLine[newMiddleLine.Count - 1], middle[i]) > segmentLength)
                {
                    if (isChanged)
                    {
                        newMiddleLine.Add(middle[i] + GeoMath.MakePointFromAngel(angle, segmentLength / 3, 0));
                        isChanged = false;
                    }
                    else
                    {
                        newMiddleLine.Add(middle[i] + GeoMath.MakePointFromAngel(angle, -(segmentLength / 3), 0));
                        isChanged = true;
                    }
                }
            }

            newMiddleLine.Add(middle[middle.Length - 1]);
            return newMiddleLine.ToArray();
        }

        public static Vector3[] CreateNewMiddleLineSegments(Vector3[] middle, float segmentLength, float heightOffset)
        {
            for (int i = 0; i < middle.Length; i++)
            {
                middle[i].Y += heightOffset;
            }

            List<Vector3> newMiddleLine = new List<Vector3>();
            //bool isChanged = false;
            newMiddleLine.Add(middle[0]);

            for (int i = 1; i < middle.Length - 1; i++)
            {
                newMiddleLine.Add(middle[i]);

                //decimal angle = GetXYAngel(newMiddleLine[newMiddleLine.Count - 1], middle[i]);
                //if (DistanceBtw2Points(newMiddleLine[newMiddleLine.Count - 1], middle[i]) > segmentLength)
                //{
                //    if (isChanged)
                //    {
                //        newMiddleLine.Add(middle[i] + makePointFromAngel(middle[i], angle, segmentLength / 3, 0));
                //        isChanged = false;
                //    }
                //    else
                //    {
                //        newMiddleLine.Add(middle[i] + makePointFromAngel(middle[i], angle, -(segmentLength / 3), 0));
                //        isChanged = true;
                //    }
                //}
            }

            newMiddleLine.Add(middle[middle.Length - 1]);
            return newMiddleLine.ToArray();
        }

        public static float[] Interpolate(Vector3[] middle, float valueStart, float valueEnd)
        {
            //valueStart = -10.0f;
            //valueEnd = 20.0f;

            float[] interpolatedValues = new float[middle.Length];
            float[] middleLineDistances = new float[middle.Length];

            interpolatedValues[0] = valueStart;

            float lengthOfStreet = 0.0f;

            for (int i2 = 1; i2 < middle.Length; i2++)
            {
                middleLineDistances[i2] = middleLineDistances[i2 - 1] + GeoMath.DistanceBtw2Points(middle[i2 - 1], middle[i2]);
                lengthOfStreet += middleLineDistances[i2];
            }

            for (int i2 = 1; i2 < interpolatedValues.Length - 1; i2++)
            {
                float percent = (middleLineDistances[i2] / middleLineDistances[middleLineDistances.Length - 1]);
                float percentStart = 1.0f - percent;
                interpolatedValues[i2] = valueStart * percentStart + (valueEnd * percent);
            }

            interpolatedValues[interpolatedValues.Length - 1] = valueEnd;

            return interpolatedValues;
        }

        public static Vector3[] Invert(Vector3[] leftLane)
        {
            Vector3[] newPositions = new Vector3[leftLane.Length];
            for (int i = 0; i < newPositions.Length; i++)
            {
                newPositions[i] = leftLane[newPositions.Length - 1 - i];
            }
            return newPositions;
        }

        #endregion Public Methods
    }
}